module src.downloader;

private import tango.net.http.HttpHeaders;
private import tango.text.convert.Format;
private import tango.io.vfs.FileFolder;
private import tango.net.http.HttpGet;
private import tango.io.device.Array;
private import tango.io.device.File;
private import tango.util.Convert;
private import tango.io.Path;
private import src.nus;

private alias char[] string;

class Downloader
{
  protected:
    NUS nus;
  
  private:
    const string NUS_WII = "http://nus.cdn.shop.wii.com/ccs/download";
    const string NUS_DSI = "http://nus.cdn.t.shop.nintendowifi.net/ccs/download";
    
    void download( string fileName )
    {
      if ( exists( nus.titleID ~ "/" ~ fileName ) )
      {
        if ( fileName == nus.TMD ~ ( nus.titleVersion !is null ? "v" ~ nus.titleVersion : "" ) )
          nus.tmdBuf = cast( ubyte[] ) File.get( nus.titleID ~ "/" ~ fileName );
        else if ( fileName == nus.CETK && nus.pack )
          nus.cetkBuf = cast( ubyte[] ) File.get( nus.titleID ~ "/" ~ fileName );
        
        return;
      }
      
      auto httpObject = new HttpGet( ( nus.isWiiTitle ? NUS_WII : NUS_DSI ) ~ "/" ~ nus.titleID ~ "/" ~ fileName );
      httpObject.getRequestHeaders.add( HttpHeader.UserAgent, "wii libnup/1.0" );
      
      auto tempPath = new FileFolder( nus.titleID, true );
      
      if ( fileName == nus.TMD ~ ( nus.titleVersion !is null ? "v" ~ nus.titleVersion : "" ) )
        nus.tmdBuf = cast( ubyte[] ) tempPath.file( fileName ).create( new Array( cast( string ) httpObject.read() ) ).input.load();
      else if ( fileName == nus.CETK && nus.pack )
        nus.cetkBuf = cast( ubyte[] ) tempPath.file( fileName ).create( new Array( cast( string ) httpObject.read() ) ).input.load();
      else
        tempPath.file( fileName ).create( new Array( cast( string ) httpObject.read() ) );
      
      tempPath.close();
      httpObject.close();
    }
    
    string trimLeadingZeros( string num )
    {
      int startindex = 0;
      
      for ( int i = 0; i < num.length; i++ )
      {
        if ( num[ i ] != 0 || num[ i ] != '0' )
          break;
        
        startindex++;
      }
      
      return num[ startindex .. ( num.length - startindex ) ];
    }
    
    string[] getContentNames( int length )
    {
      if ( nus.tmdBuf is null )
        throw new Exception( "tmd buffer is null" );
      
      int startoffset = 484;
      string[] contentnames = new string[ length ];
      
      for ( int i = 0; i < length; i++ )
      {
        contentnames[ i ]  = makeProperLength( convertToHex( to!( string )( nus.tmdBuf[ startoffset ] ) ) );
        contentnames[ i ] ~= makeProperLength( convertToHex( to!( string )( nus.tmdBuf[ startoffset + 1 ] ) ) );
        contentnames[ i ] ~= makeProperLength( convertToHex( to!( string )( nus.tmdBuf[ startoffset + 2 ] ) ) );
        contentnames[ i ] ~= makeProperLength( convertToHex( to!( string )( nus.tmdBuf[ startoffset + 3 ] ) ) );
        
        startoffset += 36;
      }
      
      return contentnames;
    }
    
    string makeProperLength( string hex )
    {
      if ( hex.length == 1 )
        hex = '0' ~ hex;
      
      return hex;
    }
    
    string convertToHex( string decval )
    {
      int uiDecimal = to!( int )( decval );
      return Format( "{0:x2}", uiDecimal );
    }
  
  public:
    this( NUS nus )
    {
      this.nus = nus;
    }
    
    void process()
    {
      download( nus.TMD );
      download( nus.CETK );
      
      string contentstrnum;
      contentstrnum ~= trimLeadingZeros( to!( string )( nus.tmdBuf[ 478 ] ) );
      contentstrnum ~= trimLeadingZeros( to!( string )( nus.tmdBuf[ 479 ] ) );
      
      nus.tmdContents = getContentNames( to!( int )( contentstrnum ) );
      
      foreach ( tmdcontent; nus.tmdContents )
      {
        download( tmdcontent );
      }
    }
}